#include "../include/func.h"

/* Usage: 服务端业务功能函数定义 */

int handle_path1(char *path,char *buf,pdir_stack_t dir_stack){
      char *p = strtok(path,"/");
      while(p != NULL){
          if(strcmp(p,".") == 0){ 
  
          }   
          else if(strcmp(p,"fu") == 0){ 
              reinit_dir_stack(dir_stack);
              traverse_dir_stack(dir_stack,buf);
              break;
          }   
          else if(strcmp(p,"..") == 0){ 
              pop_dir(dir_stack);
          }   
          else{
              traverse_dir_stack(dir_stack,buf);
              strcat(buf,p);
              break;
          }   
          p = strtok(NULL,"/");
      }   
      while(p!=NULL){
          strcat(buf,p);
          strcat(buf,"/");
          p = strtok(NULL,"/");
      }       
      return 0;
}
int handle_path(char *path,char *buf,pdir_stack_t dir_stack){
      char *p = strtok(path,"/");
      while(p != NULL){
          if(strcmp(p,".") == 0){ 
  
          }   
          else if(strcmp(p,"fu") == 0){ 
              reinit_dir_stack(dir_stack);
              traverse_dir_stack(dir_stack,buf);
              break;
          }   
          else if(strcmp(p,"..") == 0){ 
              pop_dir(dir_stack);
          }   
          else{
              traverse_dir_stack(dir_stack,buf);
   
              break;
          }   
          p = strtok(NULL,"/");
      }   
      while(p!=NULL){
          strcat(buf,p);
          strcat(buf,"/");
          p = strtok(NULL,"/");
      }       
      return 0;
}
int do_pwd(socket_t fd_client, ptrain_t ptrain, pdir_stack_t dir_stack){
    bzero(ptrain,sizeof(train_t));
    char buf[4096] = {0};
    traverse_dir_stack(dir_stack,buf);
    ptrain->data_length = strlen(buf);
    memcpy(ptrain->data,buf,ptrain->data_length);
    ptrain->tag = TAG_RESPONSEPWD;
    send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
    send(fd_client,ptrain->data,ptrain->data_length,0);
    return 0;
}

void dtype_to_type(unsigned char d_type, char *type){
    switch (d_type) {
    case DT_BLK:  *type = 'b'; break;
    case DT_CHR:  *type = 'c'; break;
    case DT_DIR:  *type = 'd'; break;
    case DT_FIFO:  *type = 'f'; break;
    case DT_LNK:  *type = 'l'; break;
    case DT_REG:  *type = '-'; break;
    case DT_SOCK:  *type = 's'; break;
    default:  *type = '?'; break;
    }   
}

int do_ls(socket_t fd_client, ptrain_t ptrain, pdir_stack_t dir_stack){
    bzero(ptrain,sizeof(train_t));
    char buf[4096] = {0};
    strcpy(buf,ROOTDIR);
    traverse_dir_stack(dir_stack,buf);
    printf("ls %s\n", buf);
    DIR *dir = opendir(buf);
    struct dirent *pdir;
    char *title = "inode\ttype\tname         \treclen\n";
    ptrain->data_length = strlen(title);
    memcpy(ptrain->data,title,ptrain->data_length);
    send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
    send(fd_client,ptrain->data,ptrain->data_length,0);
    while((pdir = readdir(dir))!=NULL){
        char content[4096] = {0};
        char type = '?';
        dtype_to_type(pdir->d_type, &type);
        sprintf(content,"%6lu\t%c\t%-15s\t%5hu\n",pdir->d_ino,type,pdir->d_name,pdir->d_reclen);
        ptrain->data_length = strlen(content);
        memcpy(ptrain->data,content,ptrain->data_length);
        send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
        send(fd_client,ptrain->data,ptrain->data_length,0);
    }
    // 目录传输结束标志
    ptrain->data_length = 0;
    send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
    return 0;
}

int do_rm(socket_t fd_client, ptrain_t ptrain, pcmd_t pcmd, dir_stack_t dir_stack){
    bzero(ptrain,sizeof(train_t));
    char buf[4096] = {0};
    strcpy(buf,ROOTDIR);
    handle_path(pcmd->cmd[1], buf, &dir_stack);

    /* char *p = strtok(pcmd->cmd[1],"/"); */
    /* while(strcmp(p,"..") == 0){ */
    /*     pop_dir(&dir_stack); */
    /*     p = strtok(NULL,"/"); */
    /* } */
    /* traverse_dir_stack(&dir_stack,buf); */
    /* strcat(buf,p); */
    /* strcat(buf,pcmd->cmd[1]); */
    
    buf[strlen(buf)-1] = '\0';
    printf("delete %s\n", buf);

    int ret = unlink(buf);
    if(ret == -1){
        char errorMsg[] = "path does not exist\n";
        ptrain->data_length = strlen(errorMsg);
        memcpy(ptrain->data,errorMsg,ptrain->data_length);
    }
    else{
        char successMsg[] = "delete file success\n";
        ptrain->data_length = strlen(successMsg);
        memcpy(ptrain->data,successMsg,ptrain->data_length);
    }
    send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
    send(fd_client,ptrain->data,ptrain->data_length,0);
    return 0;
}

int do_mkdir(socket_t fd_client, ptrain_t ptrain, pcmd_t pcmd, dir_stack_t dir_stack){
    bzero(ptrain,sizeof(train_t));
    char buf[4096] = {0};
    strcpy(buf,ROOTDIR);
    handle_path(pcmd->cmd[1], buf, &dir_stack);

    /* char *p = strtok(pcmd->cmd[1],"/"); */
    /* while(strcmp(p,"..") == 0){ */
    /*     pop_dir(&dir_stack); */
    /*     p = strtok(NULL,"/"); */
    /* } */
    /* traverse_dir_stack(&dir_stack,buf); */
    /* strcat(buf,p); */
    /* strcat(buf,pcmd->cmd[1]); */

    printf("mkdir %s\n", buf);
    int ret = mkdir(buf,0777);
    if(ret == -1){
        char errorMsg[] = "path does not exist\n";
        ptrain->data_length = strlen(errorMsg);
        memcpy(ptrain->data,errorMsg,ptrain->data_length);
    }
    else{
        char successMsg[] = "create dir success\n";
        ptrain->data_length = strlen(successMsg);
        memcpy(ptrain->data,successMsg,ptrain->data_length);
    }
    send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
    send(fd_client,ptrain->data,ptrain->data_length,0);
    return 0;
}

int do_rmdir(socket_t fd_client, ptrain_t ptrain, pcmd_t pcmd, dir_stack_t dir_stack){
    bzero(ptrain,sizeof(train_t));
    char buf[4096] = {0};
    strcpy(buf,ROOTDIR);
    handle_path(pcmd->cmd[1], buf, &dir_stack);

    /* char *p = strtok(pcmd->cmd[1],"/"); */
    /* while(strcmp(p,"..") == 0){ */
    /*     pop_dir(&dir_stack); */
    /*     p = strtok(NULL,"/"); */
    /* } */
    /* traverse_dir_stack(&dir_stack,buf); */
    /* strcat(buf,p); */
    /* strcat(buf,pcmd->cmd[1]); */

    printf("rmdir %s\n", buf);
    int ret = rmdir(buf);
    if(ret == -1){
        char errorMsg[] = "this dirent is not empty\n";
        ptrain->data_length = strlen(errorMsg);
        memcpy(ptrain->data,errorMsg,ptrain->data_length);
    }
    else{
        char successMsg[] = "delect dir success\n";
        ptrain->data_length = strlen(successMsg);
        memcpy(ptrain->data,successMsg,ptrain->data_length);
    }
    send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
    send(fd_client,ptrain->data,ptrain->data_length,0);
    return 0;
}

int do_cd(socket_t fd_client, ptrain_t ptrain, pcmd_t pcmd, pdir_stack_t dir_stack){
      char buf[4096] = {0};
      strcpy(buf,ROOTDIR);
      char *p = strtok(pcmd->cmd[1],"/");
      char *res = NULL;
      while(p != NULL){
          if(strcmp(p,".") == 0){

          }
          else if(strcmp(p,"fu") == 0){
              reinit_dir_stack(dir_stack);
              traverse_dir_stack(dir_stack,buf);
              break;
          }
          else if(strcmp(p,"..") == 0){
              pop_dir(dir_stack);
          }
          else{
              traverse_dir_stack(dir_stack,buf);
              strcat(buf,p);                                                                                                                                                   
              break;
          }
          p = strtok(NULL,"/");
      }
      res = p + strlen(p) + 1;
      strcat(buf,res);
      printf("cd %s\n", buf);
      int ret = chdir(buf);
      if(ret == -1){
          char errorMsg[] = "path is not exist\n";
          ptrain->data_length = strlen(errorMsg);
          memcpy(ptrain->data,errorMsg,ptrain->data_length);
      }
      else{
          while(p!=NULL){
              push_dir(dir_stack,p);
              p = strtok(NULL,"/");
          }
          char res[4096] = {0};
          traverse_dir_stack(dir_stack,res);
          ptrain->data_length = strlen(res);
          memcpy(ptrain->data,res,ptrain->data_length);
      }
      send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
      send(fd_client,ptrain->data,ptrain->data_length,0);
      return 0;

}

int do_cd1(socket_t fd_client, ptrain_t ptrain, pcmd_t pcmd, pdir_stack_t dir_stack){
    bzero(ptrain,sizeof(train_t));
    char buf[4096] = {0};
    strcpy(buf,ROOTDIR);
    char *p = strtok(pcmd->cmd[1],"/");
    while(p != NULL){
        if(strcmp(p,".") == 0){
        }
        else if(strcmp(p,"..") == 0){
            pop_dir(dir_stack);
        }
        else{
            break;
        }
        p = strtok(NULL,"/");
    }
    traverse_dir_stack(dir_stack,buf);
    while (p != NULL) {
        char dir[1024] = {0};
        strcpy(dir,p);
        strcat(dir,"/");
        push_dir(dir_stack,dir);
        strcat(buf,dir);
        p = strtok(NULL,"/");

    }
    /* while(p != NULL){ */
    /*     strcat(buf,p); */
    /*     strcat(buf,"/"); */
    /*     p = strtok(NULL,"/"); */
    /* } */
    /* while(strcmp(p,"..") == 0){ */
    /*     pop_dir(dir_stack); */
    /*     p = strtok(NULL,"/"); */
    /* } */
    /* traverse_dir_stack(dir_stack,buf); */
    /* strcat(buf,p); */
    /* strcat(buf,pcmd->cmd[1]); */
    //
    printf("cd path: %s\n", buf);
    int ret = chdir(buf);
    if(ret == -1){
        char errorMsg[] = "path is not exist\n";
        ptrain->data_length = strlen(errorMsg);
        memcpy(ptrain->data,errorMsg,ptrain->data_length);
    }
    else{
        char res[4096] = {0};
        traverse_dir_stack(dir_stack, res);
        ptrain->data_length = strlen(res);
        memcpy(ptrain->data,res,ptrain->data_length);
    }
    send(fd_client,&ptrain->data_length,sizeof(ptrain->data_length),0);
    send(fd_client,ptrain->data,ptrain->data_length,0);
    return 0;

}

int do_puts(socket_t fd_client, ptrain_t ptrain, pcmd_t pcmd, dir_stack_t dir_stack)
{
    bzero(ptrain,sizeof(train_t));
    //客户端是上传，服务器是接收
    //文件名是命令的第二个参数
    int ret = -1;

    //接收文件大小，如果=0，退出
    off_t filesize = 0;
    recv(fd_client, &filesize, sizeof(int), 0);
    printf("filesize = %ld\n", filesize);

    char buf[4096] = {0};
    strcpy(buf, ROOTDIR);
    char *p = strtok(pcmd->cmd[2],"/");
    while(strcmp(p,"..") == 0){
        pop_dir(&dir_stack);
        p = strtok(NULL,"/");
    }
    traverse_dir_stack(&dir_stack,buf);
    strcat(buf,p);
    strcat(buf,pcmd->cmd[2]);

    printf("puts file %s\n", buf);
    if (0 == filesize) {
        //文件有问题无法接收，退出
        return -1;
    }
    else if (filesize > HUNDRED_MEGABYTE) {
        ret = recv_file_train(fd_client, buf); //大于100M
    }
    else {
        ret = recv_file_train(fd_client, buf); //用普通方式接收
    }

    //判断接收结果
    if (-2 == ret) {
        //客户端异常断开，返回-2
        return -2;
    }
    else if (0 == ret) {
        //正常传输完毕
        printf("transfile finish.\n");
    }
    memset(ptrain, 0, sizeof(train_t));
    //回应客户端
    recv(fd_client, &ptrain->tag, sizeof(int), 0);
    ptrain->data_length = sizeof(ptrain->data);
    recv(fd_client, &ptrain->data_length, sizeof(int), 0);
    recv(fd_client, ptrain->data, ptrain->data_length, 0);
    printf("%s\n", ptrain->data);
    return 0;
}

int do_gets(socket_t fd_client, ptrain_t ptrain, pcmd_t pcmd, dir_stack_t dir_stack)
{
    bzero(ptrain,sizeof(train_t));
    //客户端是下载，服务器是发送
    //文件名是命令的第二个参数
    int ret = -1;
    char buf[4096] = {0};
    strcpy(buf, ROOTDIR);
    handle_path(pcmd->cmd[2], buf, &dir_stack);

    buf[strlen(buf)-1] = '\0';
    printf("gets file %s\n", buf);
    /* char *p = strtok(pcmd->cmd[2],"/"); */
    /* while(strcmp(p,"..") == 0){ */
    /*     pop_dir(&dir_stack); */
    /*     p = strtok(NULL,"/"); */
    /* } */
    /* traverse_dir_stack(&dir_stack,buf); */
    /* strcat(buf,p); */
    /* strcat(buf,pcmd->cmd[2]); */

    //得到文件大小
    off_t filesize = 0;
    struct stat st;
    stat(buf, &st);
    filesize = st.st_size;

    //文件类型不是常规的，就将文件大小设为0
    if (filesize && S_IFREG != (st.st_mode & S_IFMT)) {
        filesize = 0;
    }

    //发送文件大小，让客户端选择传输方式
    send(fd_client, &filesize, sizeof(int), 0);

    if (0 == filesize) {
        //没找到，退出
        printf("Not found the file!\n");
        return -1;
    }
    else if (filesize > HUNDRED_MEGABYTE * 10) {
        ret = send_file_sendfile(fd_client, buf, filesize); //>100M, 用零拷贝接口
    }
    else {
        ret = send_file_train(fd_client, buf); //用普通方式发送
    }

    //判断传输结果
    if (-2 == ret) {
        //客户端意外断开，返回-2
        return -2;
    }
    else if (0 == ret) {
        //正常传输完毕，通知客户端
        sprintf(ptrain->data, "file [%s] download succeed!", pcmd->cmd[1]);
        puts(ptrain->data);
    }
    //回应客户端
    ptrain->tag = TAG_RESPONSEGETS;
    send(fd_client, &ptrain->tag, sizeof(int), 0);
    ptrain->data_length = sizeof(ptrain->data);
    send(fd_client, &ptrain->data_length, sizeof(int), 0);
    send(fd_client, ptrain->data, ptrain->data_length, 0);
    return 0;
}

//使用私有协议传输数据，给另一个进程传输文件
int send_file_train(int socket_fd, char *filename)
{
    //打开传输文件
    int file_fd = open(filename, O_RDWR);
    ERROR_CHECK(file_fd, -1, "open");
    train_t train;	 //定义一个小货车，用来传输文件
    int ret = -1;

    //断点续传
    //从客户端接收文件大小，如果大于0就偏移文件指针
    int offset = 0;
    recv(socket_fd, &offset, sizeof(int), 0);
    if (offset) {
        lseek(file_fd, offset, SEEK_SET);
    }

    //发文件内容
    while (1) {
        memset(train.data, 0, sizeof(train.data));
        train.data_length = read(file_fd, train.data, sizeof(train.data));
        ERROR_CHECK(train.data_length, -1, "read");
        if (0 == train.data_length) {
            //传输完成，通知客户端，然后退出循环
            ret = send(socket_fd, &train.data_length, sizeof(int), 0);
            ERROR_CHECK(ret, -1, "send");
            //关闭传输文件
            close(file_fd);
            return 0;
        }

        //判断客户端异常异常退出
        ret = send(socket_fd, &train.data_length, sizeof(int), 0);
        if (-1 == ret) {
            //客户端异常断开，退出循环
            printf("Client Abnormal Exit!\n");
            //关闭传输文件
            close(file_fd);
            return -2;
        }
        ret = send(socket_fd, train.data, train.data_length, 0);
        if (-1 == ret) {
            //客户端异常断开，退出循环
            printf("Client Abnormal Exit!\n");
            //关闭传输文件
            close(file_fd);
            return -2;
        }
    }
}

//获取文件大小
int get_filesize(char *filename /*, pdir_stack_t pdir_stack*/)
{
    //遍历此目录下的所有文件
    int size = 0;
    //to_do 需要把目录改成工作路径
    DIR *dirp = opendir("/home/zyn/user_space/fu");
    struct dirent *pcur;
    while ((pcur = readdir(dirp)) != NULL) {
        if (!strcmp(filename, pcur->d_name)) {
            struct stat st;
            stat(filename, &st);
            printf("file exist!\n");
            printf("this file size: %ld\n", st.st_size);
            size = st.st_size;
            break;
        }
    }

    closedir(dirp);
    return size;
}

//小于100M的接收方式
int recv_file_train(int fd_socket, char *filename)
{
    train_t train;
    int ret = -1;

    //断点续传
    //在本地找是否有这个文件, 有发送文件大小，没有发送0
    off_t offset = get_filesize(filename);
    send(fd_socket, &offset, sizeof(int), 0);
    printf("offset = %ld\n", offset);

    //打开文件，并偏移文件指针
    int fd_file = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
    ERROR_CHECK(fd_file, -1, "open");
    if (offset) {
        lseek(fd_file, offset, SEEK_SET);
    }

    //循环接收文件内容
    while (1) {
        //重置小货车
        memset(&train, 0, sizeof(train));
        //先接数据长度
        recv(fd_socket, &train.data_length, sizeof(int), 0);
        printf("len = %d\n", train.data_length);
        if (0 == train.data_length) {
            //传输完毕
            printf("Transfer Finish!\n");
            //关闭文件
            close(fd_file);
            return 0;
        }

        //根据数据长度接收数据内容
        //防止发送方发的慢，导致接收缓冲区将车厢当成车头，设置recv参数为MSG_WAITALL
        ret = recv(fd_socket, train.data, train.data_length, MSG_WAITALL);
        if (0 == ret) {
            //对端异常断开
            close(fd_file);
            return -2;
        }

        //将接收数据写入文件
        write(fd_file, train.data, ret);
    }
}


//文件大于100M, 用sendfile发送文件
int send_file_sendfile(int fd_client, char *filename, off_t filesize){
    return 0;
} 

//大于100M，用mmap接收
int recv_file_mmap(int fd_socket, char *filename, off_t filesize){
    return 0;
} 

int login_server(int netfd)
{
    train_t train;
    char user[64];
    char inputpassword[128];
    char salt[31];
    bzero(salt, 31);
    bzero(user, 64);
    bzero(inputpassword, 128);
    while(1){
        // 接收用户名
        memset(&train, 0, sizeof(train_t));
        recv(netfd, &train.tag, sizeof(train.tag), MSG_WAITALL);
        recv(netfd, &train.data_length, sizeof(train.data_length), MSG_WAITALL);
        recv(netfd, &train.data, train.data_length, MSG_WAITALL);
        strcpy(user, train.data);
        printf("user:%s\n", user);
        // 查询用户名
        struct spwd* userinfo = getspnam(user);
        /* ERROR_CHECK(userinfo, NULL, "getspnam"); */
        if(userinfo == NULL){
            // 查无此名
            train.tag = 0;
            send(netfd, &train.tag, sizeof(int), 0);
            continue;
        }else{
            // 有这个名字
            train.tag = 1;
            send(netfd, &train.tag, sizeof(int), 0);
            break;
        }
    }
    // 获得用户加密后的密码，提取盐值
    struct spwd* userinfo = getspnam(user);
    strncpy(salt, userinfo->sp_pwdp, 30);
    salt[30] = '\0';
    printf("salt:%s\n", salt);
    // 发送盐值
    train.tag = TAGCD;
    train.data_length = sizeof(salt);
    memcpy(&train.data, salt, sizeof(salt));
    send(netfd, &train.tag, sizeof(train.tag), 0);
    send(netfd, &train.data_length, sizeof(train.data_length), 0);
    send(netfd, &train.data, train.data_length, 0);

    while(1){
        // 用inputpassword接收加密后的用户密码
        memset(&train, 0, sizeof(train_t));
        recv(netfd, &train.tag, sizeof(train.tag), MSG_WAITALL);
        recv(netfd, &train.data_length, sizeof(train.data_length), MSG_WAITALL);
        recv(netfd, &train.data, train.data_length, MSG_WAITALL);
        strcpy(inputpassword, train.data);
        printf("inputpassword:%s\n", inputpassword);
        // 利用盐值加密输入的密码，对比
        if(strcmp(inputpassword, userinfo->sp_pwdp) == 0){
            printf("password is right\n");
            train.tag = 1;
            send(netfd, &train.tag, sizeof(int), 0);
            break;
        }else{
            printf("password is wrong\n");
            train.tag = 0;
            send(netfd, &train.tag, sizeof(int), 0);
            continue;
        }
    }
    return 0;
}
